package connect4client.net;

import game.PlayerMove;

import java.io.IOException;
import java.util.concurrent.Semaphore;

import messages.*;
import connect4client.net.rmi.ConnectionControllerRemote;
import connect4client.net.servers.Server;
import connect4client.net.socket.ConnectionControllerTCP;
import connect4client.core.Controller;
import connect4client.core.MessageManager;


public abstract class ConnectionController implements Runnable {
	
	public ConnectionController(Controller controller, Server server) {
		this.runningServer = server;
		this.controller = controller;
	}
	
	public abstract void sendMessage(Message message);
	public abstract void sendMessageOrFail(Message message) throws IOException;
	public abstract boolean retry();
	
	
	public void reconnect() {

		reconnecting = true;
		
		System.err.println(Thread.currentThread());
		
		// Tries to reconnect to the server
		for (int i = 1; i <= this.RECONNECT_TRIES; i++) {
			System.out.println("Trying to reconnect to server "
					+ runningServer.getIp() + ":" + runningServer.getPort()
					+ " Try: " + i);

			if (!retry() || !reAuthenticate()) {
				try {
					Thread.sleep(this.RECONNECT_DELAY);
				} catch (InterruptedException e1) {	}
				continue;
			}
			
			break;
		}
		
		System.out.println("Connected to server " + runningServer.getIp());
		reconnecting = false;
		
		synchronized(this) {
			System.err.println("notifying...");
			this.notifyAll();
		}
	}
	
		
	public boolean reAuthenticate() {
		LoginMessage loginMessage = new LoginMessage(controller.getUser().getLogin(),controller.getUser().getPassword(),true);
		
		MessageManager messageManager = controller.getMessageManager();
		synchronized (messageManager) {
			loginMessage.setId(-1);
			loginMessage.setLastReceivedId(messageManager.getLastReceivedId());
		}
		
		sendMessage(loginMessage);
		return true;
	}
	
	
	public boolean isReconnecting() {
		return reconnecting;
	}
	
	
	public void login(String login, String password) {		
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new LoginMessage(login, password));
	}
	
	
	public void register(String login, String password) {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new RegisterMessage(login,password));
	}
	
	
	// Actions
	public void sendWallMessage(String contents){
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new WallMessage(controller.getUser().getLogin(), contents));
	}
		
	
	public void sendInviteMessage(String login,String gameName){
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new InvitationMessage(login, gameName));
	}
	
	
	public void sendInviteReplyMessage(int replyId, boolean accepted){
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new InvitationReplyMessage(replyId, accepted));
	}
	
			
	public void sendListPlayersMessage(boolean all){
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new ListPlayersMessage(all));
	}
	
	
	public void sendChatMessage(String login,String contents,int matchId){		
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new ChatMessage(controller.getUser().getLogin(),login,contents,matchId));		
	}
	
	
	public void sendPlayerMoveMessage(int matchId,PlayerMove playerMove){	
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new PlayerMoveMessage(matchId,playerMove));			
	}
	
	
	public void sendQuitMessage(){
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new QuitMessage());
	}
	
	
	public void sendGiveUpMessage(int matchId){
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new GiveUpMessage(matchId));
	}
	
	
	public void sendListMatchsMessage() {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new ListMatchsMessage());
	}
	
	
	public void sendListGamesMessage() {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new ListGamesMessage());
	}
	

	public void sendUserDetailsMessage(String login) {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new UserDetailsMessage(login));
	}

	public void sendAccountCreateMessage(String login, String password, String role) {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new AccountCreateMessage(login, password, role));
	}

	public void sendAccountEditMessage(String login, String newPassword, String newRole) {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new AccountEditMessage(login, newPassword, newRole));
	}

	public void sendAccountRemoveMessage(String login) {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new AccountRemoveMessage(login));
	}
	
	
	public void sendListPluginsMessage() {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new ListPluginsMessage());
	}
	
	
	public void sendStartPluginMessage(String pluginName) {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new StartPluginMessage(pluginName));
	}

	public void sendStopPluginMessage(String pluginName) {
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new StopPluginMessage(pluginName));
	}
	
	
	public void sendClientPongMessage(){
		MessageManager messageManager = controller.getMessageManager();
		messageManager.sendMessage(new ClientPongMessage());
	}
	
	
	public static enum CommunicationMethod {
		TCP,
		RMI
	}
	
	
	public static ConnectionController createInstance(CommunicationMethod method, Controller controller, Server server) throws IOException {
		switch (method) {
			case TCP:
				return new ConnectionControllerTCP(controller, server);
			case RMI:
				return new ConnectionControllerRemote(controller, server);
			default:
				return null;
		}
	}
	
	
	public static Semaphore loginMutex = new Semaphore(1);
	
	protected volatile boolean reconnecting = false;
	protected Controller controller;
	protected Server runningServer;
	private final int RECONNECT_TRIES = 20;
	private final int RECONNECT_DELAY = 1000; //ms	
}
